package fr.cephb.xml;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;

import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

public class XML
	{
	public static String escape(CharSequence s)
		{
		if(s==null) return null;
		try
			{
			return escape(new StringWriter(s.length()),s).toString();
			}
		catch (IOException e)
			{
			throw new RuntimeException(e);
			}
		}
	
	public static Writer escape(Writer out,CharSequence s)
		throws IOException
		{
		for(int i=0;i< s.length();++i)
            {
            switch(s.charAt(i))
                {
                case '\'': out.write("&apos;"); break;
                case '\"': out.write("&quot;"); break;
                case '&': out.write("&amp;"); break;
                case '<': out.write("&lt;"); break;
                case '>': out.write("&gt;"); break;
                default:  out.write(s.charAt(i));break;
                }
            }
		return out;
		}
	
	public static Element firstElement(Node root)
		{
		if(root==null) return null;
		for(Node n1=root.getFirstChild();
			n1!=null;
			n1=n1.getNextSibling())
			{
			if(n1.getNodeType()==Node.ELEMENT_NODE)
				return Element.class.cast(n1);
			}
		return null;
		}
	public static Element firstElement(Node root,String name)
		{
		if(root==null) return null;
		for(Node n1=root.getFirstChild();
			n1!=null;
			n1=n1.getNextSibling())
			{
			if(n1.getNodeType()==Node.ELEMENT_NODE &&
				n1.getNodeName().equals(name))
				return Element.class.cast(n1);
			}
		return null;
		}
	
	public static Element firstElement(Node root,String namespaceuri,String localName)
		{
		if(root==null) return null;
		for(Node n1=root.getFirstChild();
			n1!=null;
			n1=n1.getNextSibling())
			{
			if(n1.getNodeType()!=Node.ELEMENT_NODE) continue;
			Element e=Element.class.cast(n1);
			if(e.getNamespaceURI().equals(namespaceuri) &&
				n1.getLocalName().equals(localName))
				return e;
			}
		return null;
		}
	
	
	public static Element oneElement(Node root,String name)
		{
		Element found=null;
		if(root==null) throw new NullPointerException("root is null");
		for(Node n1=root.getFirstChild();
			n1!=null;
			n1=n1.getNextSibling())
			{
			if(n1.getNodeType()==Node.ELEMENT_NODE &&
				n1.getNodeName().equals(name))
				{
				if(found!=null) throw new IllegalArgumentException("found two <"+name+"> under <"+root.getNodeName()+">");
				found=Element.class.cast(n1);
				}
			}
		if(found==null) throw new IllegalArgumentException("found no <"+name+"> under <"+root.getNodeName()+">");
		return found;
		}
	
	public static Element oneElement(Node root)
		{
		Element found=null;
		if(root==null) throw new NullPointerException("root is null");
		for(Node n1=root.getFirstChild();
			n1!=null;
			n1=n1.getNextSibling())
			{
			if(n1.getNodeType()==Node.ELEMENT_NODE )
				{
				if(found!=null) throw new IllegalArgumentException("found two elements under <"+root.getNodeName()+">");
				found=Element.class.cast(n1);
				}
			}
		if(found==null) throw new IllegalArgumentException("found no element under <"+root.getNodeName()+">");
		return found;
		}
	
	
	/**
	 * 
	 * @param root
	 * @param namespaceuri or null as any
	 * @param localName or null as any
	 * @return
	 */
	public static Element oneElement(Node root,String namespaceuri,String localName)
		{
		Element found=null;
		if(root==null) throw new NullPointerException("root is null");
		for(Node n1=root.getFirstChild();
			n1!=null;
			n1=n1.getNextSibling())
			{
			if(n1.getNodeType()==Node.ELEMENT_NODE &&
				_filter(n1,namespaceuri,localName)
				)
				{
				if(found!=null) throw new IllegalArgumentException("found two <"+localName+"> under <"+root.getNodeName()+">");
				found=Element.class.cast(n1);
				}
			}
		if(found==null) throw new IllegalArgumentException("found no <"+localName+"> under <"+root.getNodeName()+">");
		return found;
		}
	/**
	 * 
	 * @param root
	 * @param namespaceuri or null as any
	 * @param localName or null as any
	 * @return
	 */
	public static List<Element> elements(Node root,String namespaceuri,String localName)
		{
		List<Element> array=new ArrayList<Element>();
		if(root==null) throw new NullPointerException("root is null");
		for(Node n1=root.getFirstChild();
			n1!=null;
			n1=n1.getNextSibling())
			{
			if(n1.getNodeType()==Node.ELEMENT_NODE &&
				_filter(n1,namespaceuri,localName)
				)
				{
				array.add(Element.class.cast(n1));
				}
			}
		return array;
		}
	
	
	public static List<Element> elements(Node root)
		{
		List<Element> array=new ArrayList<Element>();
		if(root==null) throw new NullPointerException("root is null");
		for(Node n1=root.getFirstChild();
			n1!=null;
			n1=n1.getNextSibling())
			{
			if(n1.getNodeType()==Node.ELEMENT_NODE)
				{
				array.add(Element.class.cast(n1));
				}
			}
		return array;
		}
	public static List<Element> elements(Node root,String nodeName)
		{
		List<Element> array=new ArrayList<Element>();
		if(root==null) throw new NullPointerException("root is null");
		for(Node n1=root.getFirstChild();
			n1!=null;
			n1=n1.getNextSibling())
			{
			if(n1.getNodeType()==Node.ELEMENT_NODE &&
			   n1.getNodeName().equals(nodeName))
				{
				array.add(Element.class.cast(n1));
				}
			}
		return array;
		}
	
	private static boolean _filter(Node root,String namespaceuri,String localName)
		{
		if(namespaceuri!=null && !namespaceuri.equals(root.getNamespaceURI())) return false;
		if(localName!=null && !localName.equals(root.getLocalName())) return false;
		return true;
		}
	
	public static String node2path(Node n)
		{StringBuilder b=new StringBuilder();
		while(n!=null)
			{
			switch(n.getNodeType())
				{
				case Node.ATTRIBUTE_NODE:
					{
					b.insert(0,"/@"+n.getNodeName());
					n= Attr.class.cast(n).getOwnerElement();
					continue;
					}
				case Node.TEXT_NODE:
					{
					b.insert(0,"text()");
					break;
					}
				case Node.COMMENT_NODE:
					{
					b.insert(0,"comment()");
					break;
					}
				case Node.ELEMENT_NODE:
					{
					int L=0;
					Node curr= n;
					while(curr!=null)
						{
						if(curr.getNodeType()==Node.ELEMENT_NODE &&
						   curr.getNodeName().equals(n.getNodeName()))
							{
							L++;
							}
						curr=curr.getPreviousSibling();
						}
					
					b.insert(0,"/"+n.getNodeName()+"["+(L)+"]");
					break;
					}
				}
			n=n.getParentNode();
			}
		return b.toString();
		}

	/** return the level of a node */
	public static int getLevel(Node n)
		{
		if(n==null) return -1;
		int L=0;
		while(n.getParentNode()!=null)
			{
			++L;
			n= n.getParentNode();
			}
		return L;
		}
	}
